package ru.geyes.screw.asm;

import ru.geyes.screw.closures.Function1N;

import java.lang.reflect.Method;

import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;

/**
 * @author alex
 *         date 29.06.2008
 *         time 23:34:52
 */
public class InvokeBuilder {
    private static final String APPLY_METHOD_NAME = "apply";
    private static final String METHOD_DESCRIPTION = "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;";

    private static final GenerationClassLoader classLoader = new GenerationClassLoader(ClassLoader.getSystemClassLoader());

    public static <R, P> Function1N<R, P> build(String className, Method m) throws IllegalAccessException, InstantiationException {

        ClassWriter cw = new ClassWriter(0);
        cw.visit(Opcodes.V1_5,
                Opcodes.ACC_PUBLIC,
                className,
                null,
                Type.getInternalName(Object.class),
                new String[]{Type.getInternalName(Function1N.class)});
        MethodVisitor methodVisitor = cw.visitMethod(Opcodes.ACC_PUBLIC, APPLY_METHOD_NAME, METHOD_DESCRIPTION, null, null);
        Class<?>[] params = m.getParameterTypes();
        int  i = 1;
        methodVisitor.visitVarInsn(Opcodes.ALOAD, i);
        for (Class<?> param : params) {
            i++;
            methodVisitor.visitVarInsn(Opcodes.ALOAD, i);
            methodVisitor.visitInsn(Opcodes.DUP);
            methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(param));
        }
        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(m.getDeclaringClass()), m.getName(), Type.getMethodDescriptor(m));
        methodVisitor.visitInsn(Opcodes.RETURN);
        methodVisitor.visitEnd();
        cw.visitEnd();
        byte[] code = cw.toByteArray();
        return (Function1N) classLoader.loadClass(className, code).newInstance();
    }
}
